package xaj.weavingtest.ast.istype;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import soot.BooleanType;
import soot.Local;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Type;
import soot.jimple.AssignStmt;
import soot.jimple.Expr;
import soot.jimple.IfStmt;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Jimple;
import soot.jimple.Stmt;
import soot.util.Chain;
import abc.soot.util.LocalGeneratorEx;
import abc.soot.util.Restructure;
import abc.weaving.tagkit.Tagger;
import abc.weaving.weaver.ConstructorInliningMap;
import abc.weaving.weaver.WeavingContext;

import abc.weaving.residues.*;

/** The dynamic residue of an if(...) pointcut
 *  @author Ganesh Sittampalam
 *  @author Ondrej Lhotak
 */ 

public class IfResidue_isType extends Residue {
	
	private String typeName;
	
    private SootMethod impl;
    
    private SootMethod implFALSE, implTRUE;
    
    private List/*<WeavingVar>*/ args;

    public Residue optimize() { return this; }
    public Residue inline(ConstructorInliningMap cim) {
        return construct(typeName, impl, implFALSE, implTRUE, WeavingVar.inline(args, cim));
    }
    private IfResidue_isType(String typeName, SootMethod impl, SootMethod implFALSE, SootMethod implTRUE, List args) {
    	this.typeName = typeName;
	this.impl=impl;
		this.implFALSE = implFALSE;
		this.implTRUE = implTRUE;
	this.args=args;
    }


    public Residue resetForReweaving() {
    	for( Iterator variableIt = args.iterator(); variableIt.hasNext(); ) {
    	    final WeavingVar variable = (WeavingVar) variableIt.next();
    		variable.resetForReweaving();
    	}
    	return this;
    }

    
    public static IfResidue_isType construct(String typeName, SootMethod impl,
    		SootMethod implFALSE, SootMethod implTRUE, List args) {
	return new IfResidue_isType(typeName, impl, implFALSE, implTRUE, args);
    }

    public String toString() {
	return "if(...)";
    }

    public Stmt codeGen
	(SootMethod method,LocalGeneratorEx localgen,
	 Chain units,Stmt begin,Stmt fail,boolean sense,
	 WeavingContext wc) {
    	
    	SootMethod metodoEscolhido = impl;
    	
    	WeavingVar wv1 = (WeavingVar) args.get(0);
    	ContextValue cv1 = MapValues.get(wv1);
    	System.out.println("IF codegen: param1=" + cv1
    		+ " type=" + cv1.getSootValue().getType()
    		+ " param2=" + typeName
    		);
    	Type st = cv1.getSootValue().getType();
    	boolean b1 = Util.isSubclass(st, typeName);
    	if (b1) {
    		metodoEscolhido = implTRUE;
    	} else {
        	boolean b2 = Util.isSubclass(typeName, st);
        	if (!b2) {
        		metodoEscolhido = implFALSE;
        	}
    	}

	List actuals=new Vector(args.size());
	Iterator it=args.iterator();
	Stmt currStmt = begin;
	while(it.hasNext()) {
		WeavingVar wv = (WeavingVar)it.next();
		Local loc = wv.get();
		// The type of the wv may not be the same as the formal type,
		// if this is a cflow variable of primitive type it will be boxed
		// In this case need to get the primitive value
		if (wv.mustBox()) {
			// The type of wv is necessarily a RefType, as wv is a boxed var
			RefType type = (RefType)wv.getType();
			
			SootClass boxClass=type.getSootClass();
			Type unboxedType = Restructure.JavaTypeInfo.getBoxingClassPrimType(boxClass);
			
			SootMethodRef unboxMethod=Scene.v().makeMethodRef
				(boxClass,
				 Restructure.JavaTypeInfo.getSimpleTypeBoxingClassMethodName(unboxedType),
	 			 new ArrayList(),
	 			 unboxedType,
	 			 false); 

			Local ifval=localgen.generateLocal(type,"ifparam");
		
			InvokeExpr unbox = Jimple.v().newVirtualInvokeExpr(loc, unboxMethod);
		
			Stmt assignstmt = Jimple.v().newAssignStmt
				(ifval, unbox);
            Tagger.tagStmt(assignstmt, wc);
				
			units.insertAfter(assignstmt, currStmt);
			currStmt = assignstmt;
			actuals.add(ifval);
		} 
		else
	    	actuals.add(loc);
	}
	
	Local ifresult=localgen.generateLocal(BooleanType.v(),"ifresult");
	InvokeExpr ifcall=Jimple.v().newStaticInvokeExpr(metodoEscolhido.makeRef(),actuals);
	AssignStmt assign=Jimple.v().newAssignStmt(ifresult,ifcall);
	Expr test;
	if(sense) test=Jimple.v().newEqExpr(ifresult,IntConstant.v(0));
	else test=Jimple.v().newNeExpr(ifresult,IntConstant.v(0));
	IfStmt abort=Jimple.v().newIfStmt(test,fail);
    Tagger.tagStmt(assign, wc);
    Tagger.tagStmt(abort, wc);
	units.insertAfter(assign,currStmt);
	units.insertAfter(abort,assign);
	return abort;
    }
}
